#region File Description
//-----------------------------------------------------------------------------
// InputState.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
// Edited by Ben Groeschen to include some mouse functionality
//-----------------------------------------------------------------------------
#endregion

#region Using Statements
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
#endregion

namespace TankWars
{
    /// <summary>
    /// Helper for reading input from keyboard, gamepad, and mouse. This class tracks
    /// both the current and previous state of each input, and implements query
    /// properties for high level input actions such as "move up through the menu"
    /// or "pause the game".
    /// </summary>
    public class InputState
    {
        #region Fields

        public const int MaxInputs = 4;

        public readonly KeyboardState[] CurrentKeyboardStates;
        public readonly GamePadState[] CurrentGamePadStates;
        private MouseState CurrentMouseState;
        private bool leftClicked;
        private bool rightClicked;

        public readonly KeyboardState[] LastKeyboardStates;
        public readonly GamePadState[] LastGamePadStates;
        private MouseState LastMouseState;

        #endregion

        #region Initialization


        /// <summary>
        /// Constructs a new input state.
        /// </summary>
        public InputState()
        {
            CurrentKeyboardStates = new KeyboardState[MaxInputs];
            CurrentGamePadStates = new GamePadState[MaxInputs];
            CurrentMouseState = new MouseState();
            leftClicked = rightClicked = false;

            LastKeyboardStates = new KeyboardState[MaxInputs];
            LastGamePadStates = new GamePadState[MaxInputs];
            LastMouseState = new MouseState();
        }


        #endregion

        #region Properties
                     

        /// <summary>
        /// Checks for "mouse over" input action from any player
        /// from mouse.  Returned are current mouse coordinates in a Vector2.
        /// </summary>
        public Vector2 CurrentMouseCoords
        {
            get
            {
                return new Vector2(CurrentMouseState.X, CurrentMouseState.Y);
            }
        }


        /// <summary>
        /// Checks for "mouse over" input action from any player
        /// from mouse.  Returned are last mouse coordinates in a Vector2.
        /// </summary>
        public Vector2 LastMouseCoords
        {
            get
            {
                return new Vector2(LastMouseState.X, LastMouseState.Y);
            }
        }


        /// <summary>
        /// Checks for a "fire main weapon" input action, from any player,
        /// on the keyboard or gamepad.
        /// </summary>
        public bool FireMainWeapon
        {
            get
            {
                return IsNewLeftMouse() ||
                       IsNewKeyHold(Keys.Space) ||
                       IsNewButtonHold(Buttons.RightShoulder) ||
                       IsNewButtonHold(Buttons.RightTrigger) ||
                       IsNewButtonHold(Buttons.A);
            }
        }

        /// <summary>
        /// Checks for a "fire main weapon" input action, from any player,
        /// on the keyboard or gamepad.
        /// </summary>
        public bool FireSecondaryWeapon
        {
            get
            {
                return IsNewRightMouse() ||
                       IsNewKeyHold(Keys.Enter) ||
                       IsNewButtonHold(Buttons.LeftShoulder) ||
                       IsNewButtonHold(Buttons.LeftTrigger) ||
                       IsNewButtonHold(Buttons.X);
            }
        }


        /// <summary>
        /// Checks for a continuous "down" input action, from any player, on either keyboard or gamepad.
        /// </summary>
        public bool GoDown
        {
            get
            {
                return IsNewKeyHold(Keys.Down) || IsNewKeyHold(Keys.S) ||
                       IsNewButtonHold(Buttons.DPadDown) ||
                       IsNewButtonHold(Buttons.LeftThumbstickDown);
            }
        }


        /// <summary>
        /// Checks for a continuous "left" input action, from any player, on either keyboard or gamepad.
        /// </summary>
        public bool GoLeft
        {
            get
            {
                return IsNewKeyHold(Keys.Left) || IsNewKeyHold(Keys.A) ||
                       IsNewButtonHold(Buttons.DPadLeft) ||
                       IsNewButtonHold(Buttons.LeftThumbstickLeft);
            }
        }


        /// <summary>
        /// Checks for a continuous "right" input action, from any player, on either keyboard or gamepad.
        /// </summary>
        public bool GoRight
        {
            get
            {
                return IsNewKeyHold(Keys.Right) || IsNewKeyHold(Keys.D) ||
                       IsNewButtonHold(Buttons.DPadRight) ||
                       IsNewButtonHold(Buttons.LeftThumbstickRight);
            }
        }


        /// <summary>
        /// Checks for a continuous "up" input action, from any player, on either keyboard or gamepad.
        /// </summary>
        public bool GoUp
        {
            get
            {
                return IsNewKeyHold(Keys.Up) || IsNewKeyHold(Keys.W) ||
                       IsNewButtonHold(Buttons.DPadUp) ||
                       IsNewButtonHold(Buttons.LeftThumbstickUp);
            }
        }


        /// <summary>
        /// Checks for a "menu up" input action, from any player,
        /// on either keyboard or gamepad.
        /// </summary>
        public bool MenuUp
        {
            get
            {
                return IsNewKeyPress(Keys.Up) || IsNewKeyPress(Keys.W) ||
                       IsNewButtonPress(Buttons.DPadUp) ||
                       IsNewButtonPress(Buttons.LeftThumbstickUp);
            }
        }


        /// <summary>
        /// Checks for a "menu down" input action, from any player,
        /// on either keyboard or gamepad.
        /// </summary>
        public bool MenuDown
        {
            get
            {
                return IsNewKeyPress(Keys.Down) || IsNewKeyPress(Keys.S) ||
                       IsNewButtonPress(Buttons.DPadDown) ||
                       IsNewButtonPress(Buttons.LeftThumbstickDown);
            }
        }
       

        /// <summary>
        /// Checks for a "menu select" input action, from any player,
        /// on the keyboard or gamepad.
        /// </summary>
        public bool MenuSelect
        {
            get
            {
                return IsNewKeyPress(Keys.Space) ||
                       IsNewKeyPress(Keys.Enter) ||
                       IsNewButtonPress(Buttons.A) ||
                       IsNewButtonPress(Buttons.Start);
            }
        }


        /// <summary>
        /// Checks for a "menu click" input action, from any player,
        /// on the mouse.
        /// </summary>
        public bool MenuClick
        {
            get
            {
                return IsNewLeftClick();
            }
        }


        /// <summary>
        /// Checks for a "menu cancel" input action, from any player,
        /// on either keyboard or gamepad.
        /// </summary>
        public bool MenuCancel
        {
            get
            {
                return IsNewKeyPress(Keys.Escape) ||
                       IsNewButtonPress(Buttons.B) ||
                       IsNewButtonPress(Buttons.Back) ||
                       IsNewRightClick();
            }
        }


        /// <summary>
        /// Checks for a "pause the game" input action, from any player,
        /// on either keyboard or gamepad.
        /// </summary>
        public bool PauseGame
        {
            get
            {
                return IsNewKeyPress(Keys.Escape) ||
                       IsNewButtonPress(Buttons.Back) ||
                       IsNewButtonPress(Buttons.Start);
            }
        }


        #endregion

        #region Methods


        /// <summary>
        /// Reads the latest state of the keyboard and gamepad.
        /// </summary>
        public void Update()
        {
            // Update keyboard and gamepad for each player
            for (int i = 0; i < MaxInputs; i++)
            {
                LastKeyboardStates[i] = CurrentKeyboardStates[i];
                LastGamePadStates[i] = CurrentGamePadStates[i];
                
                CurrentKeyboardStates[i] = Keyboard.GetState((PlayerIndex)i);
                CurrentGamePadStates[i] = GamePad.GetState((PlayerIndex)i);                
            }

            // Update mouse only once, not for each player
            LastMouseState = CurrentMouseState;
            CurrentMouseState = Mouse.GetState();
        }


        /// <summary>
        /// Helper for checking if a key was pressed and/or held
        /// during this update, by any player.
        /// </summary>
        public bool IsNewKeyHold(Keys key)
        {
            for (int i = 0; i < MaxInputs; i++)
            {
                if (IsNewKeyHold(key, (PlayerIndex)i))
                    return true;
            }

            return false;
        }


        /// <summary>
        /// Helper for checking if a key was pressed and/or held
        /// during this update, by the specified player.
        /// </summary>
        public bool IsNewKeyHold(Keys key, PlayerIndex playerIndex)
        {
            return (CurrentKeyboardStates[(int)playerIndex].IsKeyDown(key));
        }


        /// <summary>
        /// Helper for checking if a key was newly pressed during this update,
        /// by any player.
        /// </summary>
        public bool IsNewKeyPress(Keys key)
        {
            for (int i = 0; i < MaxInputs; i++)
            {
                if (IsNewKeyPress(key, (PlayerIndex)i))
                    return true;
            }

            return false;
        }


        /// <summary>
        /// Helper for checking if a key was newly pressed during this update,
        /// by the specified player.
        /// </summary>
        public bool IsNewKeyPress(Keys key, PlayerIndex playerIndex)
        {
            return (CurrentKeyboardStates[(int)playerIndex].IsKeyDown(key) &&
                    LastKeyboardStates[(int)playerIndex].IsKeyUp(key));
        }


        /// <summary>
        /// Helper for checking if a button was pressed and/or held
        /// during this update, by any player.
        /// </summary>
        public bool IsNewButtonHold(Buttons button)
        {
            for (int i = 0; i < MaxInputs; i++)
            {
                if (IsNewButtonHold(button, (PlayerIndex)i))
                    return true;
            }

            return false;
        }


        /// <summary>
        /// Helper for checking if a button was pressed and/or held
        ///  during this update, by the specified player.
        /// </summary>
        public bool IsNewButtonHold(Buttons button, PlayerIndex playerIndex)
        {
            return (CurrentGamePadStates[(int)playerIndex].IsButtonDown(button));
        }


        /// <summary>
        /// Helper for checking if a button was newly pressed during this update,
        /// by any player.
        /// </summary>
        public bool IsNewButtonPress(Buttons button)
        {
            for (int i = 0; i < MaxInputs; i++)
            {
                if (IsNewButtonPress(button, (PlayerIndex)i))
                    return true;
            }

            return false;
        }


        /// <summary>
        /// Helper for checking if a button was newly pressed during this update,
        /// by the specified player.
        /// </summary>
        public bool IsNewButtonPress(Buttons button, PlayerIndex playerIndex)
        {
            return (CurrentGamePadStates[(int)playerIndex].IsButtonDown(button) &&
                    LastGamePadStates[(int)playerIndex].IsButtonUp(button));
        }


        /// <summary>
        /// Helper for checking if the left mouse button is down during this update.
        /// </summary>
        public bool IsNewLeftMouse()
        {
            if (CurrentMouseState.LeftButton == ButtonState.Pressed)
                return true;
            else
                return false;
        }


        /// <summary>
        /// Helper for checking if the right mouse button is down during this update.
        /// </summary>
        public bool IsNewRightMouse()
        {
            if (CurrentMouseState.RightButton == ButtonState.Pressed)
                return true;
            else
                return false;
        }


        /// <summary>
        /// Helper for checking if the left mouse button was clicked during this update.
        /// </summary>
        public bool IsNewLeftClick()
        {
            if (CurrentMouseState.LeftButton == ButtonState.Pressed)
                leftClicked = true;

            if (CurrentMouseState.LeftButton == ButtonState.Released && leftClicked)
            {
                leftClicked = false;
                return true;
            }
            return false;
        }


        /// <summary>
        /// Helper for checking if the right mouse button was clicked during this update.
        /// </summary>
        public bool IsNewRightClick()
        {
            if (CurrentMouseState.RightButton == ButtonState.Pressed)
                rightClicked = true;

            if (CurrentMouseState.RightButton == ButtonState.Released && rightClicked)
            {
                rightClicked = false;
                return true;
            } 
            return false;
        }


        /// <summary>
        /// Checks for a "menu select" input action from the specified player.
        /// </summary>
        public bool IsMenuSelect(PlayerIndex playerIndex)
        {
            return IsNewKeyPress(Keys.Space, playerIndex) ||
                   IsNewKeyPress(Keys.Enter, playerIndex) ||
                   IsNewButtonPress(Buttons.A, playerIndex) ||
                   IsNewButtonPress(Buttons.Start, playerIndex);
        }

        /// <summary>
        /// Checks for a "menu click" input action from the specified player
        /// (separate from IsMenuSelect because of need to assure click position)
        /// </summary>
        public bool IsMenuClick()
        {
            return IsNewLeftClick();
        }


        /// <summary>
        /// Checks for a "menu cancel" input action from the specified player.
        /// </summary>
        public bool IsMenuCancel(PlayerIndex playerIndex)
        {
            return IsNewKeyPress(Keys.Escape, playerIndex) ||
                   IsNewButtonPress(Buttons.B, playerIndex) ||
                   IsNewButtonPress(Buttons.Back, playerIndex) ||
                   IsNewRightClick();
        }


        #endregion
    }
}
